﻿using System.Linq;
using Citi.Import.Util;
using System.Collections.Generic;
using System;
using System.Globalization;
using Citi.GocAttributes.Domain.Utils;
using System.Data;

namespace Citi.GocAttributes.Domain
{
    public partial class ManagedSegment
    {
        #region Import Validation

        private static readonly List<Definition<ManagedSegment>> Definitions = new List<Definition<ManagedSegment>>
            {
                new Definition<ManagedSegment> { ModelProperty = x => x.Code, Required= true, ColumnName = "MS ID", CustomConversionMethod = ValidateManagedSegmentId},
                new Definition<ManagedSegment> { ModelProperty = x => x.Description, Required= true, ColumnName = "DESCRIPTION"},
                new Definition<ManagedSegment> { ModelProperty = x => x.ParentId, ColumnName = "Parent Managed Segment ID", CustomConversionMethod = ValidateParentManagedSegment},
                new Definition<ManagedSegment> { ModelProperty = x => x.TreeLevel, Required= true, ColumnName = "Tree Level"},
                new Definition<ManagedSegment> { ModelProperty = x => x.Status, Required= true, ColumnName = "STATUS"},
                new Definition<ManagedSegment> { ModelProperty = x => x.UpdatedDate, Required = true, ColumnName = "Effective Date", Culture = new CultureInfo("pt-BR")}
            };

        private static readonly List<Definition<ManagedSegment>> DefinitionsWithoutParent = new List<Definition<ManagedSegment>>
            {
                new Definition<ManagedSegment> { ModelProperty = x => x.Code, Required= true, ColumnName = "MS ID", CustomConversionMethod = ValidateManagedSegmentId},
                new Definition<ManagedSegment> { ModelProperty = x => x.Description, Required= true, ColumnName = "DESCRIPTION"},
                new Definition<ManagedSegment> { ModelProperty = x => x.TreeLevel, Required= true, ColumnName = "Tree Level"},
                new Definition<ManagedSegment> { ModelProperty = x => x.Status, Required= true, ColumnName = "STATUS"},
                new Definition<ManagedSegment> { ModelProperty = x => x.UpdatedDate, Required = true, ColumnName = "Effective Date", Culture = new CultureInfo("pt-BR")}
            };

        private static object ValidateManagedSegmentId(ref ManagedSegment model, string value, List<string> fields, ref string error, List<string> headers = null)
        {
            var domain = CacheFacade.Get(SelectAll<ManagedSegment>).OrderByDescending(x => x.ReferenceDate).FirstOrDefault(x => x.Code == int.Parse(value));

            if (domain != null)
            {
                //if (!domain.Gocs.Any())
                //    model = domain;
                //else
                //{
                var description = fields[headers.IndexOf(Conversion.TransformColumnName("DESCRIPTION"))];
                var status = fields[headers.IndexOf(Conversion.TransformColumnName("STATUS"))];

                if (domain.Description == description && domain.Status == status)
                    model = domain;
                //}
            }

            return int.Parse(value);
        }

        private static object ValidateParentManagedSegment(ref ManagedSegment model, string value, List<string> fields, ref string error, List<string> headers = null)
        {
            try
            {
                var id = fields[headers.IndexOf(Conversion.TransformColumnName("MS ID"))];

                if (id == value)
                    return Conversion.Validation.Validated;

                var domain = CacheFacade.Get(SelectAll<ManagedSegment>).OrderByDescending(x => x.ReferenceDate)
                    .FirstOrDefault(x => x.Code == int.Parse(value));

                if (domain == null)
                {
                    error = string.Format("PARENT ID '{0}' NOT FOUND", value);
                    return Conversion.Validation.Failed;
                }

                model.Parent = domain;

                return Conversion.Validation.ManuallyValidated;
            }
            catch (Exception e)
            {
                error = e.Message;
                return Conversion.Validation.Failed;
            }
        }

        #endregion

        public static ImportHistory Import(string path, DateTime referenceDate)
        {
            List<Error> errors = null;
            var dataTable = Conversion.ReadExcel(path, ref errors, "Attributes", 4);

            Save(DefinitionsWithoutParent, dataTable, ref errors, referenceDate);

            errors = null;
            Save(Definitions, dataTable, ref errors, referenceDate);

            return ImportHistory.SaveHistory(errors, path);

            //var managedSegments = Conversion.ConvertFromExcel(Definitions, path, ref errors);

            //if (managedSegments != null && managedSegments.Any())
            //    SaveImport(managedSegments, referenceDate, ref errors);

            //CacheFacade.Clear(typeof(ManagedSegment));
            //return ImportHistory.SaveHistory(errors, path);
        }

        private static void Save(List<Definition<ManagedSegment>> definitions, DataTable dataTable, ref List<Error> errors, DateTime referenceDate)
        {
            var managedSegments = Conversion.Convert(definitions, dataTable, ref errors);

            if (managedSegments != null && managedSegments.Any())
            {
                managedSegments.ForEach(x =>
                {
                    x.LastUpdate = DateTime.Now;

                    if (x.Id == 0)
                        x.ReferenceDate = referenceDate;
                });

                managedSegments.First().Save(managedSegments, 800);
            }

            CacheFacade.Clear(typeof(ManagedSegment));
        }

        private static void SaveImport(List<ManagedSegment> managedSegments, DateTime referenceDate, ref List<Error> errors)
        {
            managedSegments.ForEach(x =>
                {
                    x.LastUpdate = DateTime.Now;

                    if (x.Id == 0)
                        x.ReferenceDate = referenceDate;
                });

            SaveModifieds();

            var lastTreeLevel = managedSegments.Max(x => x.TreeLevel);

            for (var level = 1; level <= lastTreeLevel; level++)
            {
                var managedsNextLevel = managedSegments.Where(x => x.TreeLevel == level).ToList();

                if (level > 1)
                {
                    var managedSegmentsPrevious = managedSegments.Where(x => x.TreeLevel == level - 1);
                    managedsNextLevel.ForEach(x =>
                    {
                        if (x.ParentId != 0 || (x.ParentId == 0 && x.Parent == null)) return;

                        var managedSegmentPrevious = managedSegmentsPrevious.FirstOrDefault(y => y.Code == x.Parent.Code);
                        if (managedSegmentPrevious == null) return;

                        x.Parent = managedSegmentPrevious;
                    });
                }

                if (managedsNextLevel.Any())
                    managedsNextLevel.First().Save(managedsNextLevel, 800);
            }
        }

        private static void SaveModifieds()
        {
            var objectStateEntries = Context.Instance.ObjectStateManager.GetObjectStateEntries(EntityState.Modified).ToList();
            objectStateEntries.AddRange(Context.Instance.ObjectStateManager.GetObjectStateEntries(EntityState.Added));

            foreach (var objectStateEntry in objectStateEntries.Where(x =>
                (((ManagedSegment)x.Entity).Description == null)
                || ((ManagedSegment)x.Entity).Parent != null && ((ManagedSegment)x.Entity).Parent.Description == null))
            {
                Context.Instance.Detach(objectStateEntry.Entity);
            }

            Context.Instance.SaveChanges();
        }

        public ManagedSegment SelectLastByCode(int code)
        {
            return SelectAll<ManagedSegment>().OrderByDescending(x => x.ReferenceDate).FirstOrDefault(x => x.Code == code);
        }

        public ManagedSegment SelectByCode(int code)
        {
            return SelectAll<ManagedSegment>().FirstOrDefault(x => x.Code == code);
        }

        //public List<ManagedSegment> SelectBrByLevelTypeResult(short level, DatabaseEnums.TypeResultType typeResultType, string typeValue)
        //{
        //    return SelectAll<ManagedSegment>().Where(x => x.Gocs.Any(y => y.Rule != null && y.Country.Abbreviation.ToUpper() == "BR"
        //            && y.Rule.RuleResults.Any(ruleResult => ruleResult.TypeRuleResult.Type == (int)typeResultType
        //                    && ruleResult.TypeRuleResult.Value.ToUpper() == typeValue.ToUpper()))
        //        && x.TreeLevel == level).ToList();
        //}
    }
}
